{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6056fc9f",
   "metadata": {},
   "source": [
    "## Graph Convolution GRU\n",
    "Standard GRU cell with Chebyshev graph convolution. <br />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2e000be",
   "metadata": {},
   "source": [
    "### GConvGRU"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "4d234a4c",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 200/200 [16:26<00:00,  4.93s/it]\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train MSE: 0.8122\n",
      "Test MSE: 0.8772\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from tqdm import tqdm\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import torch\n",
    "from torch import nn\n",
    "from torch_geometric_temporal.nn.recurrent import GConvGRU\n",
    "\n",
    "from torch_geometric_temporal.dataset import ChickenpoxDatasetLoader\n",
    "from torch_geometric_temporal.signal import temporal_signal_split\n",
    "\n",
    "loader = ChickenpoxDatasetLoader()\n",
    "\n",
    "lags = 10\n",
    "stride = 1\n",
    "epochs = 200\n",
    "\n",
    "dataset = loader.get_dataset(lags)\n",
    "\n",
    "train_dataset, test_dataset = temporal_signal_split(dataset, train_ratio=0.2)\n",
    "\n",
    "### MODEL DEFINITION\n",
    "class RecurrentGCN(nn.Module):\n",
    "    def __init__(self, node_features, window_length):\n",
    "        super(RecurrentGCN, self).__init__()\n",
    "        \n",
    "        self.node_features = node_features\n",
    "        self.n = window_length\n",
    "        self.hidden_state_size = 64\n",
    "        assert self.hidden_state_size % 2 == 0\n",
    "                \n",
    "        self.recurrent = GConvGRU(1, self.hidden_state_size, 2)\n",
    "        self.linear1 = nn.Sequential(\n",
    "            nn.Linear(self.hidden_state_size, self.hidden_state_size//2), \n",
    "            nn.Dropout(p=0.2),\n",
    "            nn.ReLU()\n",
    "        )\n",
    "        self.out = nn.Sequential(\n",
    "            nn.Linear(self.hidden_state_size//2, 1),\n",
    "            nn.Flatten(start_dim=0, end_dim=-1)\n",
    "        )\n",
    "\n",
    "    def forward(self, window, c):        \n",
    "        edge_index = window.edge_index\n",
    "        edge_weight = None\n",
    "\n",
    "        C = []\n",
    "        for t in range(self.n):\n",
    "            x = window.x[:,t].unsqueeze(0).T\n",
    "            c = self.recurrent(x, edge_index, edge_weight, c)\n",
    "            \n",
    "            C.append(c.detach())\n",
    "            \n",
    "        x = self.linear1(c)\n",
    "        pred = self.out(x)\n",
    "\n",
    "        return pred, C\n",
    "    \n",
    "model = RecurrentGCN(node_features=1, window_length=10)\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.01)\n",
    "\n",
    "\n",
    "### TRAIN\n",
    "model.train()\n",
    "\n",
    "loss_history = []\n",
    "for _ in tqdm(range(epochs)):\n",
    "    total_loss = 0\n",
    "    c = None\n",
    "    for i, window in enumerate(train_dataset):\n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        y_pred, C = model(window, c)\n",
    "        c = C[stride-1]\n",
    "\n",
    "        assert y_pred.shape == window.y.shape\n",
    "        loss = torch.mean((y_pred - window.y)**2)\n",
    "        total_loss += loss.item()\n",
    "        \n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "    total_loss /= i+1\n",
    "    loss_history.append(total_loss)\n",
    "\n",
    "### TEST \n",
    "model.eval()\n",
    "loss = 0\n",
    "with torch.no_grad():\n",
    "    c = None\n",
    "    for i, window in enumerate(test_dataset):\n",
    "        y_pred, C = model(window, c)\n",
    "        c = C[stride-1]\n",
    "        \n",
    "        assert y_pred.shape == window.y.shape\n",
    "        loss += torch.mean((y_pred - window.y)**2)\n",
    "    loss /= i+1\n",
    "\n",
    "### RESULTS PLOT\n",
    "fig, ax = plt.subplots()\n",
    "\n",
    "x_ticks = np.arange(1, epochs+1)\n",
    "ax.plot(x_ticks, loss_history)\n",
    "\n",
    "# figure labels\n",
    "ax.set_title('Loss over time', fontweight='bold')\n",
    "ax.set_xlabel('Epochs', fontweight='bold')\n",
    "ax.set_ylabel('Mean Squared Error', fontweight='bold')\n",
    "plt.show()\n",
    "    \n",
    "print(\"Train MSE: {:.4f}\".format(total_loss))\n",
    "print(\"Test MSE: {:.4f}\".format(loss))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f72cd9b",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "9dc1092468b6c95b716c12f4a89881ec47ccb10a5d5fac9cc7256670f9aa8e17"
  },
  "kernelspec": {
   "display_name": "Python 3.7.0 64-bit ('py37': conda)",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
